package fileoperate

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/shirou/gopsutil/v3/disk"
	"gitlab.local/DO-module/new-filemanage-module/src/common"
	"gitlab.local/DO-module/new-filemanage-module/src/constant"
	"gitlab.local/DO-module/new-filemanage-module/src/controller/errcode"
	"gitlab.local/DO-module/new-filemanage-module/src/core"
	"gitlab.local/DO-module/new-filemanage-module/src/core/fileutils"
	"gitlab.local/DO-module/new-filemanage-module/src/core/sqlitedb/sqlitemodel"
	"gitlab.local/DO-module/new-filemanage-module/src/service/file_safeBox"
	"gitlab.local/TerraMaster/tos-modules/core/acl"
	fileopreate2 "gitlab.local/TerraMaster/tos-modules/fileoperate"
	"io/fs"
	"math"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/gogf/gf/errors/gcode"
	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/os/glog"
	"gitlab.local/golibrary/linux"
	"gitlab.local/golibrary/user"
	"gitlab.local/golibrary/utils"
	"golang.org/x/net/context"
)

var FileClassifyList = map[string]FType{
	"oexe": Oexe,                                                                                                                                                                                                                                                                                                                                                                                                 //快捷方式
	"svg":  Image, "jpg": Image, "jpeg": Image, "png": Image, "gif": Image, "webp": Image, "raf": Image, "crw": Image, "cr2": Image, "kdc": Image, "mrw": Image, "nef": Image, "orf": Image, "dng": Image, "ptx": Image, "pef": Image, "arw": Image, "x3f": Image, "rw2": Image, "tif": Image, "tiff": Image, "bmp": Image, "heic": Image, "heif": Image, "jxr": Image, "psd": Image, "ico": Image, "dwg": Image, //图像
	"mp4": Video, "m4v": Video, "mkv": Video, "webm": Video, "mov": Video, "avi": Video, "wmv": Video, "mpg": Video, "flv": Video, "3gp": Video, //视频
	"mid": Audio, "mp3": Audio, "m4a": Audio, "ogg": Audio, "flac": Audio, "wav": Audio, "amr": Audio, "aac": Audio, "aiff": Audio, //音频
}

type FType string

const ( //文件分类
	Folder FType = "folder" //文件夹
	File   FType = "file"   //普通文件
	Oexe   FType = "oexe"   //快捷方式
	Audio  FType = "audio"  //音频
	Video  FType = "video"  //视频
	Image  FType = "image"  //图像
)

// BackgroundTaskLog 后台任务日志
type BackgroundTaskLog struct {
	TaskId      string                              `json:"task_id"`      //id
	Type        fileopreate2.FileOperation          `json:"type"`         //类型
	Name        string                              `json:"name"`         //名称
	Size        int64                               `json:"size"`         //大小
	Rate        float32                             `json:"rate"`         //进度
	Status      fileopreate2.FileOperationRunStatus `json:"status"`       //状态
	ErrorMsg    string                              `json:"error_msg"`    //错误信息
	FileTotal   int64                               `json:"file_total"`   //文件总数量
	CurrentSize int64                               `json:"current_size"` //当前已完成大小(B)
	ToPath      string                              `json:"to_path"`      //目标文件夹路径
}

// OrderRule 排序规则
type OrderRule string

const (
	Time   OrderRule = "time"   //时间排序
	Size   OrderRule = "size"   //大小排序
	Nature OrderRule = "nature" //自然排序
)

var VolumeRegexP = regexp.MustCompile(`^/Volume\d+($|/)`)

// GetShareList 获取共享文件目录
func (f *FileOperate) GetShareList() ([]*fileopreate2.FoldersInfo, error) {
	data := make([]*fileopreate2.FoldersInfo, 0)
	//查询数据库
	var folders []sqlitemodel.Share
	_ = f.db.DbGet().Find(&folders)
	for _, item := range folders {
		if item.Type == "ISO" || item.Ecryptfs == 1 {
			if dfItem := linux.GetDeviceInfoByPath(item.MntPath); dfItem == nil {
				continue
			}
		}
		folderInfo, e := f.GetFolder(item.MntPath)
		if e != nil {
			continue
		}
		folderInfo.IsHome = 2
		data = append(data, folderInfo)
	}
	return data, nil
}

// GetShareOfUsb 获取共享文件目录
func (f *FileOperate) GetShareOfUsb() ([]*fileopreate2.FoldersInfo, error) {
	data := make([]*fileopreate2.FoldersInfo, 0)
	//查询数据库
	var folders []sqlitemodel.Share
	_ = f.db.DbGet().Where("Device=?", "@usb").Find(&folders)

	for _, item := range folders {
		if item.Type == "ISO" || item.Ecryptfs == 1 {
			if dfItem := linux.GetDeviceInfoByPath(item.MntPath); dfItem == nil {
				continue
			}
		}
		folderInfo, e := f.GetFolder(item.MntPath)
		if e != nil {
			continue
		}
		folderInfo.Volume = strings.TrimPrefix(strings.TrimSuffix(VolumeRegexP.FindString(folderInfo.Path), "/"), "/")
		data = append(data, folderInfo)
	}
	return data, nil
}

// GetFolders 获取路径下文件及文件夹信息
// limit 条数
// page 页码
// rule 排序规则 time size
// order true降序 false为升序
func (f *FileOperate) GetFolders(dir string, limit, page int, rule fileopreate2.OrderRule, order, noShowHiddenFolder bool, safeBoxSrc string, safeBoxStatus bool) (*fileopreate2.Folders, error) {
	data := &fileopreate2.Folders{
		Total: 0,
		Data:  make([]fileopreate2.FoldersInfo, 0),
	}

	info, err := os.Stat(dir)
	if err != nil {
		return data, gerror.Wrap(err, "GetFolders")
	}
	//判断oexe文件
	if filepath.Ext(dir) == ".oexe" {
		content, err := utils.GetOexeDate(dir)
		if err != nil {
			return data, gerror.Wrap(err, "GetFolders1")
		}
		dir = content.Link
	}
	if !info.IsDir() {
		//文件不处理
		return data, nil
	}

	data.Data, data.Total, err = f.folderDirSort(dir, limit, page, rule, order, noShowHiddenFolder, safeBoxSrc, safeBoxStatus)
	if err != nil {
		return data, gerror.Wrap(err, "GetFolders2")
	}

	return data, nil
}

// folderDirSort 文件目录排序
// order true降序 false为升序
// safeBoxSrc 保险箱路径（为空不会显示保险箱文件夹）
// return 排序后的文件绝对路径(文件夹始终排在前面)
func (f *FileOperate) folderDirSort(dir string, limit, page int, rule fileopreate2.OrderRule, order, noShowHiddenFolder bool, safeBoxSrc string, safeBoxStatus bool) ([]fileopreate2.FoldersInfo, int, error) {
	var result []fileopreate2.FoldersInfo

	file, err := os.Open(dir)
	if err != nil {
		return result, 0, err
	}

	names, err := file.Readdirnames(-1)
	_ = file.Close()
	if err != nil {
		return result, 0, err
	}

	var folders []os.FileInfo
	var files []os.FileInfo

	//匹配保险箱路径

	for _, v := range names {
		if strings.HasPrefix(v, constant.IgnoreFilePre) {
			//忽略文件或文件夹
			continue
		}
		if noShowHiddenFolder && strings.HasPrefix(v, ".") {
			//跳过隐藏文件夹
			continue
		}
		path := filepath.Join(dir, v)
		//保险箱路径处理
		if common.SafeBoxRegex.MatchString(path) {
			continue
		}
		if safeBoxSrc == path && !safeBoxStatus {
			continue
		}

		//针对符号连接判断
		stat, err := os.Stat(path)
		if err != nil {
			continue
		}

		if stat.IsDir() {
			folders = append(folders, stat)
		} else {
			files = append(files, stat)
		}
	}

	if rule != "" {
		//对文件和文件夹分别排序
		var wg sync.WaitGroup
		wg.Add(2)

		go func() {
			defer wg.Done()
			folders = f.Sort(dir, folders, rule, order)
		}()

		go func() {
			defer wg.Done()
			files = f.Sort(dir, files, rule, order)
		}()

		wg.Wait()
	}
	total := len(folders) + len(files)
	if limit > 0 {
		folders = f.chunkFolders(page, limit, folders, files)
	} else {
		folders = append(folders, files...)
	}

	if len(folders) == 0 {
		return result, total, nil
	}

	result = f.getFileInfoList(folders, dir, safeBoxSrc)

	return result, total, nil
}

// chunkFolders 截取查询所需的limit个数的数据
func (f *FileOperate) chunkFolders(page, limit int, folders, files []os.FileInfo) []os.FileInfo {
	// 预分配足够的容量以减少append操作
	var result []os.FileInfo
	result = make([]os.FileInfo, 0, limit)

	// 先处理folders
	foldersStart := (page - 1) * limit
	foldersEnd := foldersStart + limit

	// 检查folders切片是否有足够的元素
	foldersCount := len(folders)
	if foldersCount > foldersStart {
		if foldersEnd > foldersCount {
			foldersEnd = foldersCount // 调整结束索引，以避免超出folders的长度
		}
		result = append(result, folders[foldersStart:foldersEnd]...)

		// 如果folders已经满足了limit要求，那么就不需要再考虑files
		if len(result) >= limit {
			return result[:limit] // 截断结果，以确保不超过limit
		}
	}

	// 计算还需要从files中获取多少个元素
	remainingLimit := limit - len(result)

	// 接下来处理files
	filesStart := (page - 1) * limit // 对于files，我们从同一页开始
	filesEnd := filesStart + remainingLimit

	// 检查files切片是否有足够的元素
	filesCount := len(files)
	if filesCount > filesStart {
		if filesEnd > filesCount {
			filesEnd = filesCount // 调整结束索引，以避免超出files的长度
		}
		result = append(result, files[filesStart:filesEnd]...)

		// 如果结果超过limit，截断它
		if len(result) > limit {
			return result[:limit]
		}
	}

	// 如果files仍然没有填满结果，但我们已经处理完了所有可用的项，则返回当前结果
	return result
}

// getFileInfoList 获取文件或文件夹详细信息
func (f *FileOperate) getFileInfoList(folders []os.FileInfo, dir, safeBoxSrc string) []fileopreate2.FoldersInfo {
	var mu sync.Mutex
	var wg sync.WaitGroup
	result := make([]fileopreate2.FoldersInfo, 0, len(folders))

	numWorkers := int(math.Ceil(float64(len(folders)) / float64(100)))
	wg.Add(numWorkers)

	// 计算每个子切片的大小
	chunkSize := (len(folders) + numWorkers - 1) / numWorkers // 确保所有folders都会被处理

	// 创建一个通道，用于传递工作给goroutines
	workCh := make(chan os.FileInfo, numWorkers)
	ctx, _ := context.WithCancel(context.Background())
	// 启动工作分发goroutine
	go func() {
		defer close(workCh)

		start := 0
		for start < len(folders) {
			end := start + chunkSize
			if end > len(folders) {
				end = len(folders)
			}
			for _, v := range folders[start:end] {
				select {
				case <-ctx.Done():
					return // 退出分发
				case workCh <- v:
				}
			}
			start = end
		}
	}()

	// 启动工作处理goroutines
	for i := 0; i < numWorkers; i++ {
		go func() {
			defer wg.Done()
			select {
			case <-ctx.Done():
				return // 退出goroutine
			default:
				for v := range workCh {
					// 处理文件信息
					path := filepath.Join(dir, v.Name())

					//folder, err := f.GetFolder(path)
					folder, err := f.GetFolderInfo(path, v)
					if path == filepath.Dir(safeBoxSrc) {
						folder.IsHome = 1
					}
					if err == nil {
						mu.Lock()
						result = append(result, *folder)
						mu.Unlock()
					}
				}
			}
		}()
	}

	wg.Wait()

	return result
}

// GetFolderInfo 获取文件或者文件夹基本信息
func (f *FileOperate) GetFolderInfo(path string, fi os.FileInfo) (*fileopreate2.FoldersInfo, error) {
	return f.fileInfo(path, fi)
}

// Sort 排序
func (f *FileOperate) Sort(dir string, dirs []os.FileInfo, rule fileopreate2.OrderRule, order bool) []os.FileInfo {

	sort.SliceStable(dirs, func(i, j int) bool {
		switch rule {
		case fileopreate2.Time:
			if dirs[i].ModTime().After(dirs[j].ModTime()) {
				if order { //a>b 为降序时触发
					return true
				}
			} else {
				if !order { //a<b 为升序时触发
					return true
				}
			}
		case fileopreate2.Size:
			var a, b int64
			if dirs[i].IsDir() {
				a, _ = f.FolderSize(filepath.Join(dir, dirs[i].Name()), nil)
			} else {
				a = dirs[i].Size()
			}
			if dirs[j].IsDir() {
				b, _ = f.FolderSize(filepath.Join(dir, dirs[j].Name()), nil)
			} else {
				b = dirs[j].Size()
			}
			if a > b {
				if order { //a>b 为降序时触发
					return true
				}
			} else {
				if !order { //a<b 为升序时触发
					return true
				}
			}
		case fileopreate2.Nature:
			if dirs[i].Name() > dirs[j].Name() {
				if order { //a>b 为降序时触发
					return true
				}
			} else {
				if !order { //a<b 为升序时触发
					return true
				}
			}
		}

		return false
	})

	return dirs
}

func (f *FileOperate) GetHomeVolume() string {
	homeVolume := ""
	df, err := disk.Partitions(false)
	if err != nil {
		return ""
	}

	//获取home挂载卷
	for _, v := range df {
		if v.Mountpoint == "/home" {
			homeVolume = v.Device
			break
		}
	}
	for _, v := range df {
		if v.Device == homeVolume {
			homeVolume = v.Mountpoint
			break
		}
	}
	return strings.Trim(homeVolume, "/")
}

// GetFolder 获取文件或者文件夹基本信息
func (f *FileOperate) GetFolder(path string) (*fileopreate2.FoldersInfo, error) {
	data := &fileopreate2.FoldersInfo{}

	info, err := utils.CustomStat(path)
	if err != nil {
		return data, gerror.Wrap(err, "GetFolder")
	}
	return f.fileInfo(path, info)
}

// fileInfo 获取文件或者文件夹基本信息
func (f *FileOperate) fileInfo(path string, info os.FileInfo) (*fileopreate2.FoldersInfo, error) {
	data := &fileopreate2.FoldersInfo{}
	dateLayout, timeLayout := core.New().GetTimeFormat()
	layOut := fmt.Sprintf("%s %s", dateLayout, timeLayout)

	// 转换为系统相关的文件信息
	sys := info.Sys().(*syscall.Stat_t)

	// 获取文件创建时间
	createTime := time.Unix(sys.Ctim.Unix())
	if !createTime.IsZero() {
		data.CTime = createTime.Format(layOut)
	}
	data.Name = info.Name()
	data.Path = path
	data.ATime = time.Unix(sys.Atim.Sec, sys.Atim.Nsec).Format(layOut)
	data.MTime = info.ModTime().Format(layOut)
	data.Volume = strings.TrimPrefix(strings.TrimSuffix(VolumeRegexP.FindString(path), "/"), "/")
	if data.Volume == "" {
		data.Volume = f.GetHomeVolume()
	}
	if info.IsDir() {
		data.FType = utils.Folder
	} else {
		//获取文件真实类型(直接获取文件后缀)
		data.FRealType = strings.ToLower(strings.TrimPrefix(filepath.Ext(path), "."))
		data.FType, _ = extFileClassify(data.FRealType)
		data.Size = info.Size()
		data.SizeFriendly = utils.FileSizeFriendly(data.Size)
		if data.FType == utils.Oexe {
			d, err := utils.GetOexeDate(path)
			if err == nil {
				data.Link = d.Link
				data.Icon = d.Icon
			}
		}
	}
	return data, nil
}

func extFileClassify(ext string) (utils.FType, error) {
	if len(utils.FileClassifyList) == 0 {
		utils.FileClassifyList = map[string]utils.FType{
			"oexe": utils.Oexe,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   //快捷方式
			"svg":  utils.Image, "jpg": utils.Image, "jpeg": utils.Image, "png": utils.Image, "gif": utils.Image, "webp": utils.Image, "raf": utils.Image, "crw": utils.Image, "cr2": utils.Image, "kdc": utils.Image, "mrw": utils.Image, "nef": utils.Image, "orf": utils.Image, "dng": utils.Image, "ptx": utils.Image, "pef": utils.Image, "arw": utils.Image, "x3f": utils.Image, "rw2": utils.Image, "tif": utils.Image, "tiff": utils.Image, "bmp": utils.Image, "heic": utils.Image, "heif": utils.Image, "jxr": utils.Image, "psd": utils.Image, "ico": utils.Image, "dwg": utils.Image, //图像
			"mp4": utils.Video, "m4v": utils.Video, "mkv": utils.Video, "webm": utils.Video, "mov": utils.Video, "avi": utils.Video, "wmv": utils.Video, "mpg": utils.Video, "flv": utils.Video, "3gp": utils.Video, //视频
			"mid": utils.Audio, "mp3": utils.Audio, "m4a": utils.Audio, "ogg": utils.Audio, "flac": utils.Audio, "wav": utils.Audio, "amr": utils.Audio, "aac": utils.Audio, "aiff": utils.Audio, //音频
		}
	}
	tmp := strings.ToLower(ext)
	fmt.Println("方法内处理后参数", tmp)
	fType, b := utils.FileClassifyList[tmp]
	if b {
		return fType, nil
	}
	fmt.Println("==========", utils.File)
	return utils.File, nil
}

// GetFoldersFilter 获取用户拥有文件或者文件夹详细信息(带过滤)
func (f *FileOperate) GetFoldersFilter(dir, username string, ctx context.Context) (*fileopreate2.FoldersInfoAll, error) {
	data := &fileopreate2.FoldersInfoAll{}

	all, err := f.GetFolderAll(dir, username, ctx)
	if err != nil {
		return nil, err
	}
	data.FoldersInfo = *all
	data.SuperAdmin = os.Getenv("admin")
	return data, nil
}

func translatePermissionToInt(access string) int {
	ret := 0
	for i := 0; i < len(access); i++ {
		switch access[i] {
		case 'r':
			ret += 4
		case 'w':
			ret += 2
		case 'x':
			ret += 1
		}
	}
	return ret
}

// GetFolderAll 获取用户拥有文件或者文件夹详细信息
func (f *FileOperate) GetFolderAll(dir, username string, ctx context.Context) (*fileopreate2.FoldersInfo, error) {
	data, err := f.GetFolder(dir)
	if err != nil {
		return nil, gerror.Wrap(err, "GetFolderAll")
	}
	path := common.EscapeChars(dir)
	u, g, err := f.aclService.GetPathAclUserOrGroup(path)
	if err != nil {
		return nil, gerror.Wrap(err, "GetFolderAll1")
	}

	oriAcl, err := f.aclService.GetAll(path)

	aclUser := make(map[string]string)
	all, err := f.aclService.GetUserOrGroup()
	if err != nil {
		return data, gerror.Wrap(err, "GetFolderAll")
	}
	userMapAcl := make(map[string]string)
	userMapAclType := make(map[string]int)
	for _, a := range oriAcl.Acl {
		if a.Type == "other" {
			userMapAcl[a.Type] = a.Permission
		}

		uname := a.Name
		if a.Type == "group" {
			uname = "@" + a.Name
		}
		userMapAcl[uname] = a.Permission
		userMapAclType[uname] = a.AclType
	}

	//获取管理员
	var out bytes.Buffer
	cmd := exec.Command("sh", "-c", "grep ':0:' /etc/passwd | cut -d ':' -f 1")
	cmd.Stdout = &out
	cmd.Run()
	systemUser := strings.TrimRight(out.String(), "\n")
	mid, _ := f.aclService.GetAclTypeByFile(data.Path)
	for k, v := range all {
		ak := "@" + k
		maxAclType := int(acl.PosixAcl)
		for _, val := range v {
			if val == systemUser {
				aclUser[val] = userMapAcl[systemUser]
				continue
			}
			var ok bool
			if _, ok = userMapAcl[val]; !ok && aclUser[val] != "define" {
				aclUser[val], maxAclType = acl.AclRight(userMapAcl[ak]).Gte(aclUser[val], acl.AclType(userMapAclType[ak]), acl.AclType(maxAclType))
			}

			if ok {
				aclUser[val] = userMapAcl[val]
			}
		}
		aclPermission, _ := userMapAcl[ak]
		acltype := int(acl.PosixAcl)
		if val, ok := mid[ak]; ok {
			acltype = val
			if acltype == int(acl.RichAcl) {
				aclPermission = ""
			}
		}
		if aclPermission == "" {
			aclPermission = "---"
		}
		data.Groups = append(data.Groups, acl.Attrs{
			Type:       "group",
			Name:       k,
			Permission: aclPermission,
			Effective:  aclPermission,
			AclType:    acltype,
		},
		)
	}

	f.fileUsers(data, userMapAcl, aclUser, mid)
	/*for k, v := range aclGroup {
		acltype := int(acl.PosixAcl)
		k = strings.TrimLeft(k, "@")
		if val, ok := mid["@"+k]; ok {
			acltype = val
			if acltype == int(acl.RichAcl) {
				v = ""
			}
		}
		if v == "" {
			v = "---"
		}
		data.Groups = append(data.Groups, acl.Attrs{
			Type:       "group",
			Name:       k,
			Permission: v,
			Effective:  v,
			AclType:    acltype,
		},
		)
	}*/

	data.Owner = u
	data.Group = g
	data.VolumePath, _ = utils.SwitchShowName(dir)
	data.Permission, _ = f.aclService.UserAcl(dir, username)
	data.SizeFriendly = utils.FileSizeFriendly(data.Size)

	//名称排序
	sort.Slice(data.Users, func(i, j int) bool {
		return data.Users[j].Name > data.Users[i].Name
	})
	sort.Slice(data.Groups, func(i, j int) bool {
		return data.Groups[j].Name > data.Groups[i].Name
	})

	return data, nil
}

// 获取用户权限
func (f *FileOperate) fileUsers(data *fileopreate2.FoldersInfo, userMapAcl, user map[string]string, mid map[string]int) {
	for k, v := range user {
		acltype := int(acl.PosixAcl)
		if val, ok := mid[k]; ok {
			acltype = val
			if acltype == int(acl.RichAcl) {
				v = ""
			}
		}
		groupAcl := ""
		if _, ok := userMapAcl[k]; !ok {
			otherPer := translatePermissionToInt(userMapAcl["other"])
			groupPer := translatePermissionToInt(v)

			groupAcl = v
			v = ""

			if otherPer > groupPer {
				groupAcl = ""
				v = userMapAcl["other"]
			}
		}
		data.Users = append(data.Users, fileopreate2.UserAttrs{
			Groups: f.aclService.UserGroups(k),
			Attrs: acl.Attrs{
				Type:       "user",
				Name:       k,
				Permission: v,
				Effective:  v,
				AclType:    acltype,
			},
			GroupAcl: groupAcl,
		})
	}
}

// FolderSize 获取文件或文件夹占用总大小（B）
func (f *FileOperate) FolderSize(path string, ctx context.Context) (int64, error) {
	var (
		res string
		err error
	)

	s := fmt.Sprintf(`du -sb %s | awk '{print $1}'`, utils.StrSpecialLetters(path))
	if ctx == nil {
		res, err = utils.ShellExec(s)
	} else {
		res, err = utils.ShellWithContext(ctx, s)
	}
	if err != nil {
		return 0, err
	}

	return strconv.ParseInt(strings.TrimSpace(res), 10, 64)
}

// fileQuantity 获取目录下 文件数量 文件夹数量
// path 绝对路径
// return 文件数量 文件夹数量 错误
func (f *FileOperate) fileQuantity(dir string, ctx context.Context) (int, int) {
	var fileQuantity, folderQuantity int

	_ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, _ error) error {
		select {
		case <-ctx.Done():
			return errors.New("termination")
		default:
		}
		if dir == path { //排除根目录
			return nil
		}
		if d.IsDir() {
			folderQuantity++
		} else {
			fileQuantity++
		}
		return nil
	})

	return fileQuantity, folderQuantity
}

// DelFolders 删除文件及文件夹
func (f *FileOperate) DelFolders(dirs ...string) error {
	if len(dirs) == 1 {
		return f.delFolders(dirs[0])
	}
	for _, v := range dirs {
		_ = f.delFolders(v)
	}
	return nil
}

// delFolders 删除文件
func (f *FileOperate) delFolders(dir string) error {
	if !utils.Exists(dir) {
		return nil
	}
	return os.RemoveAll(dir)
}

// FolderRenameFilter 文件或文件夹重命名(过滤)
func (f *FileOperate) FolderRenameFilter(dir, rName, uname string, t ...int8) (*fileopreate2.FoldersInfo, error) {
	data, e := f.FolderRename(dir, rName, uname, t...)
	if e != nil {
		return nil, e
	}
	p, e := utils.SwitchShowName(data.Path)
	if e != nil {
		return nil, e
	}
	data.VolumePath = p
	//层级
	data.Level = utils.FileLevel(data.Path)
	return data, nil
}

// FolderRename 文件或文件夹重命名
// dir旧文件路径,rName新文件名称
// 非oexe文件无法使用.oexe扩展名
// t为1时,oexe文件的扩展名始终会为.oexe
func (f *FileOperate) FolderRename(dir, rName, uname string, t ...int8) (*fileopreate2.FoldersInfo, error) {
	//判断文件有没有权限
	if !f.aclService.IsWrite(dir, uname) {
		return nil, gerror.NewCode(errcode.PermissionDenied, dir)
	}

	info, e := os.Stat(dir)
	if e != nil {
		return nil, gerror.Wrap(e, "FolderRename")
	}

	//保险箱特殊处理
	lookup, err := user.Lookup(uname)
	if err != nil {
		return nil, err
	}
	reg := regexp.MustCompile(fmt.Sprintf(`^%s/?`, fmt.Sprintf(file_safeBox.MountSafeBoxDir, lookup.HomeDir)))
	safeBoxSrc := reg.FindString(dir)
	if safeBoxSrc == dir {
		return nil, gerror.NewCode(errcode.SafeModification, dir)
	}

	if safeBoxSrc != filepath.Dir(dir)+"/" {
		if !f.aclService.IsWrite(filepath.Dir(dir), uname) {
			return nil, gerror.NewCode(errcode.PermissionDenied, filepath.Dir(dir))
		}
	}

	if safeBoxSrc != "" {
		_ = f.safeBox.SafeBoxUnLock(safeBoxSrc)
		defer f.safeBox.SafeBoxLock(safeBoxSrc)
	}
	if !f.aclService.IsWrite(filepath.Dir(dir), uname) {
		return nil, gerror.NewCode(errcode.PermissionDenied, filepath.Dir(dir))
	}

	//匹配共享文件夹路径
	reg2 := regexp.MustCompile(`^/Volume\d+/(@usb/usbshare\d+|@iso|@iscsi|@eSATA|[^/]+)/`)
	dirPath, filePath := filepath.Split(dir)

	sharePath := reg2.FindString(dir)
	if sharePath != "" && strings.TrimRight(dirPath, "/") == strings.TrimRight(sharePath, "/") { //不能重命名#recycle
		if filePath == "#recycle" || rName == "#recycle" {
			return nil, errors.New("cannot operate recycle bin path")
		}
	}

	if !info.IsDir() {
		ext := filepath.Ext(dir)
		s := strings.HasSuffix(rName, ".oexe")
		//判断oexe文件
		if ext == ".oexe" {
			if len(t) > 0 && t[0] == 1 && !s {
				rName += ".oexe"
			}
		} else if s {
			return nil, errors.New("a no oexe file cannot be modified to an oexe file")
		}
	}

	nDir := filepath.Join(dirPath, rName)
	if utils.Exists(nDir) {
		return nil, gerror.NewCode(errcode.SourcePathExist)
	}
	//重命名
	if e = os.Rename(dir, nDir); e != nil {
		return nil, gerror.Wrap(e, "FolderRename11")
	}
	//读取路径信息
	r, e := f.GetFolder(nDir)
	if e != nil {
		return nil, gerror.Wrap(e, "FolderRename22")
	}

	return r, nil
}

// VolumeUserQuota 判断用户容量是否超出限制
// size 目前需要容量(B)
// return 是否超出
func (f *FileOperate) VolumeUserQuota(path, uname string, size int64) bool {
	mountpoint := fileutils.Mountpoint(path)

	// 获取home目录所在卷(home目录只受ext4卷容量限制)
	if strings.HasPrefix(path, "/home/") {
		mountpoint = linux.GetMainVolume()
	}

	var available int64
	var err error
	if fsType, err := fileutils.DetermineType(mountpoint); err == nil && fsType == fileutils.EXT4 {
		// ext4 格式的文件系统查询用户的 quota
		_, available, err = fileutils.QueryQuota(uname, mountpoint)
		if errors.Is(err, fileutils.ErrMountpointNotFound) || available == -1 {
			// 没有 quota 按文件系统可用空间
			_, available, err = fileutils.DiskSpaceUsage(mountpoint)
		}
	} else {
		// 其他格式的文件系统直接按可用空间
		_, available, err = fileutils.DiskSpaceUsage(mountpoint)
	}
	if err != nil {
		glog.Error(err)
		return true
	}
	return available < size
}

// CreateFolderFilter 新建文件或文件夹(过滤)
// t 1文件，2文件夹
func (f *FileOperate) CreateFolderFilter(dir string, t int8, uname string) (*fileopreate2.FoldersInfo, error) {

	if f.VolumeUserQuota(dir, uname, 0) {
		return nil, gerror.NewCode(errcode.InsufficientHardDiskCapacity)
	}

	parent := dir
	for {
		parent = filepath.Dir(parent)
		if parent == "/" || utils.Exists(parent) {
			break
		}
	}

	//保险箱特殊处理
	if f.safeBox != nil {
		lookup, err := user.Lookup(uname)
		if err != nil {
			return nil, err
		}
		reg := regexp.MustCompile(fmt.Sprintf(`^%s/?`, fmt.Sprintf(file_safeBox.MountSafeBoxDir, lookup.HomeDir)))
		safeBoxSrc := reg.FindString(dir)
		if safeBoxSrc != "" {
			_ = f.safeBox.SafeBoxUnLock(safeBoxSrc)
			defer f.safeBox.SafeBoxLock(safeBoxSrc)
		}
	}

	//由于dir为完整路径,所以判断父级目录权限
	if !f.aclService.IsWrite(parent, uname) {
		return nil, gerror.NewCode(errcode.PermissionDenied, parent)
	}
	_ = utils.SwitchUserRight()

	// 保存不存在路径用于继承设置文件夹所属用户
	notExitPath := f.getNotExistPath(dir)

	folder, err := f.CreateFolder(dir, t)
	if err != nil {
		return nil, gerror.Wrap(err, "Create folder fail")
	}
	p, err := utils.SwitchShowName(folder.Path)
	if err != nil {
		return nil, gerror.Wrap(err, "Switch show name fail")
	}
	folder.VolumePath = p
	//层级
	folder.Level = utils.FileLevel(folder.Path)

	// 新建的文件夹需要设置拥有者
	for _, p := range notExitPath {
		//修改文件或目录拥有者
		_ = f.aclService.Chown(p, uname)
	}

	//继承父级目录权限
	_ = f.aclService.InheritedPermissions(folder.Path, parent, false)

	return folder, nil
}

// getNotExistPath Get not exist paths
// for example: /home/user1/test/test1/test2,the system only has /home/user1/test
// so the result is ["/home/user1/test/test1","/home/user1/test/test1/test2"]
func (f *FileOperate) getNotExistPath(path string) []string {
	result := make([]string, 0)
	pathArray := strings.Split(path, "/")
	currentPath := ""
	pathArray = pathArray[1:]
	for _, dir := range pathArray {
		currentPath += "/" + dir
		if _, err := os.Lstat(currentPath); os.IsNotExist(err) {
			result = append(result, currentPath)
		}
	}
	return result
}

// CreateFolder 创建文件或文件夹
// t 1文件，2文件夹
func (f *FileOperate) CreateFolder(dir string, t int8) (*fileopreate2.FoldersInfo, error) {
	switch t {
	case 1:
		flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
		// xfs卷使用追加的形式写入
		if fileutils.IsXfsVolume(dir) {
			flag = os.O_WRONLY | os.O_CREATE | os.O_APPEND
		}
		file, err := os.OpenFile(dir, flag, os.ModePerm)
		if err != nil {
			return nil, gerror.Wrap(err, "CreateFolder")
		}
		_ = file.Close()
	case 2:
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			return nil, gerror.Wrap(err, "Create dir fail")
		}
	default:
		return nil, errors.New("operation type error")
	}
	return f.GetFolder(dir)
}

// GetBackgroundTaskLog 获取单条后台任务日志
func (f *FileOperate) GetBackgroundTaskLog(tid int64) *fileopreate2.BackgroundTaskLog {
	task := f.getTask(tid)
	if task == nil {
		return nil
	}
	taskName := ""
	if len(task.paths) > 0 {
		taskName = filepath.Base(task.paths[0])
	}

	errorMsg := ""
	if task.Err != nil {
		errorMsg = task.Err.Error()
	}

	go f.calculateProgress(task) //运行进度统计

	return &fileopreate2.BackgroundTaskLog{
		TaskId:      strconv.FormatInt(tid, 10),
		Type:        task.Type,
		Name:        taskName,
		Rate:        task.Rate,
		Size:        task.Size,
		ErrorMsg:    errorMsg,
		Status:      task.Status,
		FileTotal:   task.FileTotal,
		CurrentSize: task.CurrentSize,
		ToPath:      task.ToPath,
	}
}

// GetBackgroundTaskLogs 获取后台任务日志
func (f *FileOperate) GetBackgroundTaskLogs(uname string) ([]fileopreate2.BackgroundTaskLog, error) {
	data := make([]fileopreate2.BackgroundTaskLog, 0)

	for _, v := range f.getUserAllTask(uname) {
		taskName := ""
		if v.taskName != "" {
			taskName = v.taskName
		} else {
			if len(v.paths) > 0 {
				taskName = filepath.Base(v.paths[0])
			}
		}

		errorMsg := ""
		if v.Err != nil {
			errorMsg = v.Err.Error()
		}

		data = append(data, fileopreate2.BackgroundTaskLog{
			TaskId:      strconv.FormatInt(v.TaskId, 10),
			Type:        v.Type,
			Name:        taskName,
			Rate:        v.Rate,
			Size:        v.Size,
			ErrorMsg:    errorMsg,
			Status:      v.Status,
			FileTotal:   v.FileTotal,
			CurrentSize: v.CurrentSize,
			ToPath:      v.ToPath,
		})
		go f.calculateProgress(v) //运行进度统计
	}

	sort.SliceStable(data, func(i, j int) bool {
		return data[i].TaskId > data[j].TaskId
	})

	return data, nil
}

// SetBackgroundTaskLogsMsg 获取后台任务日志
func (f *FileOperate) SetBackgroundTaskLogsMsg(uname string, totalSize int64, totalFile uint64, path string, size int64) {
	for _, v := range f.getUserAllTask(uname) {
		if v.Size == totalSize && v.FileTotal == int64(totalFile) {
			v.CurrentSize += size
			if v.CurrentSize > v.Size {
				v.CurrentSize = v.Size
			}
			f.fileOperationRWMutex.Lock()
			f.fileOperationTask[v.TaskId] = v
			f.fileOperationRWMutex.Unlock()
		}
	}
}

// MoveFiles 移动文件或文件夹(包含子目录),如果文件同名会被覆盖
// t 1.复制 2移动
// log 是否开启日志记入
func (f *FileOperate) MoveFiles(uname string, pa []string, outDir string, t fileopreate2.FileOperation, isSafeBox bool) (int64, error) {
	file, e := os.Stat(outDir)
	if e != nil {
		return 0, gerror.Wrap(e, "MoveFiles1")
	}
	if !file.IsDir() {
		return 0, gerror.Wrap(e, "MoveFiles2")
	}

	lookup, err := user.Lookup(uname)
	if err != nil {
		return 0, err
	}

	//保险箱特殊处理
	safeBoxSrc := ""
	if f.safeBox != nil {
		reg := regexp.MustCompile(fmt.Sprintf(`^%s/?`, fmt.Sprintf(file_safeBox.MountSafeBoxDir, lookup.HomeDir)))
		for _, v := range append(pa, outDir) {
			safeBoxSrc = reg.FindString(v)
			if safeBoxSrc != "" {
				break
			}
		}
	}

	if safeBoxSrc != "" {
		_ = f.safeBox.SafeBoxUnLock(safeBoxSrc)
	}

	//先判断权限
	//来源可读-写
	for _, dir := range pa {
		if !f.aclService.IsWrite(dir, uname) {
			if safeBoxSrc != "" {
				_ = f.safeBox.SafeBoxLock(safeBoxSrc)
			}
			if t == 2 {
				return 0, gerror.NewCode(errcode.PermissionDenied, dir)
			}

		}
		if t == 2 {
			if !f.aclService.IsWrite(filepath.Dir(dir), uname) {
				if safeBoxSrc != "" {
					_ = f.safeBox.SafeBoxLock(safeBoxSrc)
				}
				return 0, gerror.NewCode(errcode.PermissionDenied, filepath.Dir(dir))
			}
		}

	}

	//目标可写
	if !f.aclService.IsWrite(outDir, uname) {
		if safeBoxSrc != "" {
			_ = f.safeBox.SafeBoxLock(safeBoxSrc)
		}
		return 0, gerror.NewCode(gcode.New(401, outDir, nil), "permission denied")
	}

	var taskId int64

	if isSafeBox {
		e = f.moveFileNoLog(pa, outDir, safeBoxSrc)
	} else {
		switch t {
		case fileopreate2.Move, fileopreate2.Copy:
			taskParams := fileopreate2.TaskParams{}
			taskParams.Uname = uname
			taskParams.TType = t
			taskParams.ToPath = outDir
			taskParams.Paths = pa
			taskParams.SafeBoxSrc = safeBoxSrc
			taskId, e = f.RunFileOperationTask(taskParams)
		default:
			return 0, errors.New("no operation type")
		}

		if safeBoxSrc != "" {
			if e != nil || (e == nil && taskId == 0) {
				_ = f.safeBox.SafeBoxLock(safeBoxSrc)
			}
		}
	}

	return taskId, e
}

func (f *FileOperate) moveFileNoLog(paths []string, outDir, safeBoxSrc string) error {
	for _, v := range paths {
		if filepath.Dir(v) == outDir { //同目录不操作
			continue
		}
		if err := os.Rename(v, filepath.Join(outDir, filepath.Base(v))); err != nil {
			return gerror.Wrap(err, "moveFileNoLog")
		}
	}
	_ = f.safeBox.SafeBoxLock(safeBoxSrc)
	return nil
}

// StopAllMvOrCp 停止全部移动或复制任务
func (f *FileOperate) StopAllMvOrCp(uname string) error {
	for _, v := range f.getUserAllTask(uname) {
		go f.removeFileOperationTask(v.TaskId)
	}
	return nil
}

// StopBackgroundTask 停止后台任务StopBackgroundTask
func (f *FileOperate) StopBackgroundTask(data []string) error {
	for _, v := range data {
		i, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return gerror.Wrap(err, "StopBackgroundTask")
		}
		go f.removeFileOperationTask(i)
	}
	return nil
}

// GetFolderSize 获取文件或文件夹占用大小
// return 0运行中,1完成 容量（B）
func (f *FileOperate) GetFolderSize(path string, ctx context.Context) (uint8, int64, int, int) {
	f.sizeRWMutex.RLock()
	size, b := f.sizeTask[path]
	f.sizeRWMutex.RUnlock()
	if b { //任务存在
		switch size.status {
		case 1: //完成
			size.delChan <- struct{}{}
			f.sizeRWMutex.Lock()
			delete(f.sizeTask, path)
			f.sizeRWMutex.Unlock()
			return 1, size.capacity, size.FileQuantity, size.FolderQuantity
		case 0: //运行中,给任务重置10s时间
			if !size.timer.Stop() {
				<-size.timer.C
				_ = size.timer.Reset(time.Second * 10)
			}
		}
	} else {
		go f.addFolderSizeTask(path, ctx)
	}
	return 0, 0, 0, 0
}

// addFolderSizeTask 添加获取文件或文件夹占用大小任务
func (f *FileOperate) addFolderSizeTask(path string, ctx context.Context) {
	cs := &CapacityStatistics{}
	cancel, cancelFunc := context.WithCancel(context.Background())
	cs.cancelFunc = &cancelFunc
	cm := exec.CommandContext(cancel, `/bin/bash`, `-c`, fmt.Sprintf(`du -sb %s | awk '{print $1}'`, utils.StrSpecialLetters(path)))
	var output []byte
	status := make(chan struct{})
	cs.timer = time.NewTimer(time.Second * 10)
	f.sizeRWMutex.Lock()
	f.sizeTask[path] = cs
	f.sizeRWMutex.Unlock()

	go func() {
		output, _ = cm.Output()
		status <- struct{}{}
	}()

	select {
	case <-cs.timer.C: //超时,终止运行,删除任务
		cancelFunc()
		f.sizeRWMutex.Lock()
		delete(f.sizeTask, path)
		f.sizeRWMutex.Unlock()
		return
	case <-status: //运行结束
		close(status)
		cs.status = 1
		_ = cs.timer.Stop()
	}

	cs.capacity, _ = strconv.ParseInt(string(bytes.TrimSpace(output)), 10, 64)
	cs.delChan = make(chan struct{})
	cs.FileQuantity, cs.FolderQuantity = f.fileQuantity(path, ctx)
	_ = cs.timer.Reset(time.Second * 10) //结果只保留10s

	select {
	case <-cs.timer.C:
		f.sizeRWMutex.Lock()
		delete(f.sizeTask, path)
		f.sizeRWMutex.Unlock()
	case <-cs.delChan:
		_ = cs.timer.Stop()
	}
	close(cs.delChan)
}

// GetUserAuthority
// @Description: 根据用户名获取当前文件权限
// @receiver f
// @param path
// @param uname
// @return bool
// @return error
func (f *FileOperate) GetUserAuthority(path, uname string) ([]string, error) {
	acls, err := f.aclService.GetAllUserOrGroupAcl(path)
	if err != nil {
		return nil, err
	}
	return acls.User[uname], nil
}
